package com.lecyon.farm.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.lecyon.farm.base.BaseResponse;
import com.lecyon.farm.base.RestResponse;
import com.lecyon.farm.comment.JwtToken;
import com.lecyon.farm.comment.JwtUtil;
import com.lecyon.farm.common.constant.CommonConstant;
import com.lecyon.farm.common.constant.FarmSysConstant;
import com.lecyon.farm.common.enums.UserStatusEnum;
import com.lecyon.farm.common.exception.FarmException;
import com.lecyon.farm.entity.JsSysRole;
import com.lecyon.farm.entity.JsSysUser;
import com.lecyon.farm.entity.JsSysUserRole;
import com.lecyon.farm.mapper.JsSysRoleMapper;
import com.lecyon.farm.mapper.JsSysUserMapper;
import com.lecyon.farm.mapper.JsSysUserRoleMapper;
import com.lecyon.farm.service.ITuoguanClient;
import com.lecyon.farm.util.RedisUtils;
import com.lecyon.farm.util.ResultUtil;
import com.lecyon.farm.util.StringHelper;
import com.lecyon.farm.util.ValidateUtils;
import com.lecyon.farm.util.codec.EncodeUtils;
import com.lecyon.farm.util.codec.Sha1Utils;
import com.lecyon.farm.vo.query.QueryJsSysUserVo;
import com.lecyon.farm.vo.request.LoginVo;
import com.lecyon.farm.vo.request.LogoutVo;
import com.lecyon.farm.vo.view.JsSysUserVo;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.Map;

/**
 * 权限相关 service
 *
 * @author Yao Zheng
 * @Date 2019/9/5 15:52
 */
@Service
@Validated
public class AuthenticationService extends ServiceImpl<JsSysUserMapper, JsSysUser> {

    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationService.class);

    private RedisUtils redisUtils;

    private JsSysUserRoleMapper sysUserRoleMapper;

    private JsSysRoleMapper sysRoleMapper;

    private ITuoguanClient tuoguanClient;

    public static final String HASH_ALGORITHM = "SHA-1";
    public static final int HASH_INTERATIONS = 1024;
    public static final int SALT_SIZE = 8;

    /**
     * 登录
     *
     * @param loginVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public RestResponse<JwtToken> login(LoginVo loginVo) {
        String username = loginVo.getUserName();
        String password = loginVo.getPassword();
        JsSysUser sysUser = findUserByUserName(username);
        if (ValidateUtils.isNullOrEmpty(sysUser) || StringHelper.isEmpty(sysUser.getLoginCode())) {
            return ResultUtil.error(null, CommonConstant.EX_CLIENT_INVALID, "不存在的用户");
        }
        // 真正的密文密码
        String realPassword = sysUser.getPassword();
        // 待验证的明文密码
        String plain = EncodeUtils.decodeHtml(password);
        byte[] salt = EncodeUtils.decodeHex(realPassword.substring(0, 16));
        byte[] hashPassword = Sha1Utils.sha1(plain.getBytes(), salt, HASH_INTERATIONS);
        String validatePassword = EncodeUtils.encodeHex(salt) + EncodeUtils.encodeHex(hashPassword);
        if (!validatePassword.equals(realPassword)) {
            return ResultUtil.error(null, CommonConstant.EX_USER_LOGIN, "用户名或密码错误");
        }
        if (StringHelper.isEmpty(sysUser.getStatus()) || !UserStatusEnum.ACTIVE.getStatusCode().equals(sysUser.getStatus())) {
            //如果用户状态为空或者不为“正常”
            return ResultUtil.error(null, CommonConstant.EX_USER_INVALID, "该用户已被停用，无权使用该系统");
        }
        String account = sysUser.getLoginCode();
        //获取系统用户编码，用于redis的key
        String sysUserId = sysUser.getUserCode();
        StringBuilder key = new StringBuilder();
        key.append(CommonConstant.SYSTEM_NAME).append(CommonConstant.SYSTEM_SPLIT_STR).append(sysUserId);
        String loginUserKey = key.toString();
        //用户令牌
        String token;
        boolean flag = redisUtils.hasKey(loginUserKey);
        if (flag) {
            //如果redis中有该用户的令牌，则使用现有令牌
            token = (String) redisUtils.hGet(loginUserKey, CommonConstant.CONTEXT_KEY_USER_TOKEN);
            redisUtils.expire(loginUserKey, CommonConstant.MAX_REDIS_TOKEN_STORAGE_TIME);
        } else {
            //根据用户名和密码和sysUserId、生成token令牌
            token = JwtUtil.sign(username, password, sysUserId, sysUser.getLoginCode());
        }
        Map<String, Object> redisHash = Maps.newHashMap();
        redisHash.put(CommonConstant.CONTEXT_KEY_USER_TOKEN, token);
        List<String> roles = getUserRoleNameListByUserName(account);
        if (ValidateUtils.isNotNullOrEmpty(sysUser)) {
            redisHash.put(CommonConstant.CONTEXT_KEY_USERNAME, sysUser.getUserName());
            redisHash.put(CommonConstant.CONTEXT_KEY_USER_ID, sysUser.getUserCode());
            redisHash.put(CommonConstant.CONTEXT_KEY_USER, JSON.toJSON(sysUser));
            redisHash.put(CommonConstant.CONTEXT_KEY_USER_ROLES, roles);
        }
        //将token令牌存放到redis中
        flag = redisUtils.hmSet(loginUserKey, redisHash, CommonConstant.MAX_REDIS_TOKEN_STORAGE_TIME);
        if (!flag) {
            return ResultUtil.error(null, CommonConstant.EX_DB_REDIS_ERROR, "Redis异常，请稍后再试");
        }
        //同时将token作为key，将 account 作为value 存储到redis中
        key.delete(0, key.length());
        key.append(CommonConstant.SYSTEM_NAME).append("_").append(token);
        String tokenKey = key.toString();
        flag = redisUtils.hasKey(tokenKey);
        if (!flag) {
            flag = redisUtils.set(tokenKey, JSON.toJSON(sysUser), CommonConstant.MAX_REDIS_TOKEN_STORAGE_TIME);
            if (!flag) {
                throw new FarmException(CommonConstant.EX_DB_REDIS_ERROR, "Redis 操作异常,请联系系统管理员");
            }
        }
        // 对农机托管平台发送登录指令
        String response = tuoguanClient.login(loginVo.getUserName(), loginVo.getPassword(), null, "on", "on");
        LOG.info(response);
        return ResultUtil.success(null, token);
    }

    /**
     * 登出当前用户
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse logout(LogoutVo vo) {
        String token = vo.getUserToken();
        if (StringHelper.isEmpty(token)) {
            return ResultUtil.error(CommonConstant.EX_FORM_VALIDATE_REQUIRED, "当前用户令牌缺失");
        }
        StringBuilder key = new StringBuilder();
        String itemKey = JwtUtil.getUsername(token);
        boolean flag = redisUtils.hHasKey(FarmSysConstant.REDIS_KEY_LOGIN_USER_TABLE, itemKey);
        if (flag) {
            redisUtils.hDel(FarmSysConstant.REDIS_KEY_LOGIN_USER_TABLE, itemKey);
        }
        key.delete(0, key.length());
        key.append(CommonConstant.SYSTEM_NAME).append(CommonConstant.SYSTEM_SPLIT_STR).append(token);
        String tokenKey = key.toString();
        flag = redisUtils.hasKey(tokenKey);
        if (flag) {
            redisUtils.del(tokenKey);
        }
        return ResultUtil.success(null, null);
    }


    /**
     * 根据登录名查找用户信息
     *
     * @param loginCode 账号
     * @return 用户信息
     */
    public JsSysUser findUserByUserName(String loginCode) {
        JsSysUser result = null;
        if (StringHelper.isEmpty(loginCode)) {
            return null;
        }
        QueryWrapper<JsSysUser> wrapper = new QueryWrapper<>();
        wrapper.eq("login_code", loginCode);
        int count = count(wrapper);
        if (count == 1) {
            result = getOne(wrapper);
        }
        return result;
    }

    /**
     * 根据 token令牌 查找用户信息
     *
     * @param vo 查询条件
     * @return 用户信息
     */
    public RestResponse<JsSysUserVo> getUserInfoByToken(QueryJsSysUserVo vo) {
        RestResponse<JsSysUserVo> result = new RestResponse<>();
        if (StringHelper.isEmpty(vo.getUserToken())) {
            LOG.error("getUserInfoByToken method invoked. The necessary condition is missed. token is empty");
            return ResultUtil.error(result, CommonConstant.EX_FORM_VALIDATE_REQUIRED, "缺少令牌");
        }
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(CommonConstant.SYSTEM_NAME).append(CommonConstant.SYSTEM_SPLIT_STR).append(vo.getUserToken());
        String key = stringBuilder.toString();
        boolean redisFlag = redisUtils.hasKey(key);
        if (!redisFlag) {
            LOG.error("Can not found any record with condition token ：{}", vo.getUserToken());
            return ResultUtil.error(result, CommonConstant.EX_FORM_VALIDATE_REQUIRED, "找不到该用户");
        }
        JsSysUserVo resultVo = new JsSysUserVo();
        JSON value = (JSON) redisUtils.get(key);
        JsSysUser jsSysUser = JSON.toJavaObject(value, JsSysUser.class);
        BeanUtils.copyProperties(jsSysUser, resultVo);
        return ResultUtil.success(result, resultVo);
    }

    /**
     * 根据用户账号获取对应的角色
     *
     * @param loginCode 账号
     * @return 角色列表
     */
    public List<JsSysRole> getUserRoleListByUserName(String loginCode) {
        JsSysUser sysUser = findUserByUserName(loginCode);
        if (ValidateUtils.isNullOrEmpty(sysUser) || StringHelper.isEmpty(sysUser.getUserCode())) {
            return null;
        }
        String userId = sysUser.getUserCode();
        List<String> roleIdList = Lists.newArrayList();
        List<JsSysRole> result = null;
        QueryWrapper<JsSysUserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.eq("user_code", userId);
        List<JsSysUserRole> sysUserRoleList = sysUserRoleMapper.selectList(userRoleQueryWrapper);
        if (CollectionUtils.isNotEmpty(sysUserRoleList)) {
            sysUserRoleList.stream()
                    .filter(item -> item != null)
                    .forEach(item -> {
                        roleIdList.add(item.getRoleCode());
                    });
        }
        if (CollectionUtils.isNotEmpty(roleIdList)) {
            result = sysRoleMapper.selectBatchIds(roleIdList);
        }
        return result;
    }

    /**
     * 根据用户账号获取对应的角色
     *
     * @param loginCode 账号
     * @return 角色列表
     */
    public List<String> getUserRoleNameListByUserName(String loginCode) {
        List<String> result = Lists.newLinkedList();
        List<JsSysRole> roleList = getUserRoleListByUserName(loginCode);
        if (CollectionUtils.isNotEmpty(roleList)) {
            roleList.stream().filter(item -> item != null)
                    .forEach(item -> {
                        if (StringHelper.isNotEmpty(item.getRoleCode())) {
                            result.add(item.getRoleCode());
                        }
                    });
        }
        return result;
    }

//    /**
//     * 根据用户名查对应的权限信息
//     *
//     * @param userName 账号
//     * @return 权限
//     */
//    public List<String> getUserPermissionListByUserName(String userName) {
//        List<JsSysRole> roleList = getUserRoleListByUserName(userName);
//        List<String> result = Lists.newLinkedList();
//        if (CollectionUtils.isNotEmpty(roleList)) {
//            roleList.stream().filter(item -> item != null)
//                    .forEach(item -> {
//                        if (StringHelper.isNotEmpty(item.getCorpCode())) {
//                            result.add(item.getName());
//                        }
//                    });
//        }
//        return result;
//    }


    /**
     * ----------------------------- 分割线 以下是setter方法，用于替换field上的@Autowired -----------------------------
     **/

    @Autowired
    @Lazy
    public void setRedisUtils(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    @Autowired
    @Lazy
    public void setSysUserRoleMapper(JsSysUserRoleMapper sysUserRoleMapper) {
        this.sysUserRoleMapper = sysUserRoleMapper;
    }

    @Autowired
    @Lazy
    public void setSysRoleMapper(JsSysRoleMapper sysRoleMapper) {
        this.sysRoleMapper = sysRoleMapper;
    }

    @Autowired
    @Lazy
    public void setTuoguanClient(ITuoguanClient tuoguanClient) {
        this.tuoguanClient = tuoguanClient;
    }
}
